package ntlmssp
import (
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/hex"
"errors"
"strings"
"time"
)
type authenicateMessage struct {
LmChallengeResponse []byte
NtChallengeResponse []byte
TargetName string
UserName string
EncryptedRandomSessionKey []byte
NegotiateFlags negotiateFlags
MIC []byte
}
type authenticateMessageFields struct {
messageHeader
LmChallengeResponse varField
NtChallengeResponse varField
TargetName varField
UserName varField
Workstation varField
_ [8 ]byte
NegotiateFlags negotiateFlags
}
func (m authenicateMessage ) MarshalBinary () ([]byte , error ) {
if !m .NegotiateFlags .Has (negotiateFlagNTLMSSPNEGOTIATEUNICODE ) {
return nil , errors .New ("Only unicode is supported" )
}
target , user := toUnicode (m .TargetName ), toUnicode (m .UserName )
workstation := toUnicode ("" )
ptr := binary .Size (&authenticateMessageFields {})
f := authenticateMessageFields {
messageHeader : newMessageHeader (3 ),
NegotiateFlags : m .NegotiateFlags ,
LmChallengeResponse : newVarField (&ptr , len (m .LmChallengeResponse )),
NtChallengeResponse : newVarField (&ptr , len (m .NtChallengeResponse )),
TargetName : newVarField (&ptr , len (target )),
UserName : newVarField (&ptr , len (user )),
Workstation : newVarField (&ptr , len (workstation )),
}
f .NegotiateFlags .Unset (negotiateFlagNTLMSSPNEGOTIATEVERSION )
b := bytes .Buffer {}
if err := binary .Write (&b , binary .LittleEndian , &f ); err != nil {
return nil , err
}
if err := binary .Write (&b , binary .LittleEndian , &m .LmChallengeResponse ); err != nil {
return nil , err
}
if err := binary .Write (&b , binary .LittleEndian , &m .NtChallengeResponse ); err != nil {
return nil , err
}
if err := binary .Write (&b , binary .LittleEndian , &target ); err != nil {
return nil , err
}
if err := binary .Write (&b , binary .LittleEndian , &user ); err != nil {
return nil , err
}
if err := binary .Write (&b , binary .LittleEndian , &workstation ); err != nil {
return nil , err
}
return b .Bytes (), nil
}
func ProcessChallenge (challengeMessageData []byte , user , password string , domainNeeded bool ) ([]byte , error ) {
if user == "" && password == "" {
return nil , errors .New ("Anonymous authentication not supported" )
}
var cm challengeMessage
if err := cm .UnmarshalBinary (challengeMessageData ); err != nil {
return nil , err
}
if cm .NegotiateFlags .Has (negotiateFlagNTLMSSPNEGOTIATELMKEY ) {
return nil , errors .New ("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)" )
}
if cm .NegotiateFlags .Has (negotiateFlagNTLMSSPNEGOTIATEKEYEXCH ) {
return nil , errors .New ("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)" )
}
if !domainNeeded {
cm .TargetName = ""
}
am := authenicateMessage {
UserName : user ,
TargetName : cm .TargetName ,
NegotiateFlags : cm .NegotiateFlags ,
}
timestamp := cm .TargetInfo [avIDMsvAvTimestamp ]
if timestamp == nil {
ft := uint64 (time .Now ().UnixNano ()) / 100
ft += 116444736000000000
timestamp = make ([]byte , 8 )
binary .LittleEndian .PutUint64 (timestamp , ft )
}
clientChallenge := make ([]byte , 8 )
rand .Reader .Read (clientChallenge )
ntlmV2Hash := getNtlmV2Hash (password , user , cm .TargetName )
am .NtChallengeResponse = computeNtlmV2Response (ntlmV2Hash ,
cm .ServerChallenge [:], clientChallenge , timestamp , cm .TargetInfoRaw )
if cm .TargetInfoRaw == nil {
am .LmChallengeResponse = computeLmV2Response (ntlmV2Hash ,
cm .ServerChallenge [:], clientChallenge )
}
return am .MarshalBinary ()
}
func ProcessChallengeWithHash (challengeMessageData []byte , user , hash string ) ([]byte , error ) {
if user == "" && hash == "" {
return nil , errors .New ("Anonymous authentication not supported" )
}
var cm challengeMessage
if err := cm .UnmarshalBinary (challengeMessageData ); err != nil {
return nil , err
}
if cm .NegotiateFlags .Has (negotiateFlagNTLMSSPNEGOTIATELMKEY ) {
return nil , errors .New ("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)" )
}
if cm .NegotiateFlags .Has (negotiateFlagNTLMSSPNEGOTIATEKEYEXCH ) {
return nil , errors .New ("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)" )
}
am := authenicateMessage {
UserName : user ,
TargetName : cm .TargetName ,
NegotiateFlags : cm .NegotiateFlags ,
}
timestamp := cm .TargetInfo [avIDMsvAvTimestamp ]
if timestamp == nil {
ft := uint64 (time .Now ().UnixNano ()) / 100
ft += 116444736000000000
timestamp = make ([]byte , 8 )
binary .LittleEndian .PutUint64 (timestamp , ft )
}
clientChallenge := make ([]byte , 8 )
rand .Reader .Read (clientChallenge )
hashParts := strings .Split (hash , ":" )
if len (hashParts ) > 1 {
hash = hashParts [1 ]
}
hashBytes , err := hex .DecodeString (hash )
if err != nil {
return nil , err
}
ntlmV2Hash := hmacMd5 (hashBytes , toUnicode (strings .ToUpper (user )+cm .TargetName ))
am .NtChallengeResponse = computeNtlmV2Response (ntlmV2Hash ,
cm .ServerChallenge [:], clientChallenge , timestamp , cm .TargetInfoRaw )
if cm .TargetInfoRaw == nil {
am .LmChallengeResponse = computeLmV2Response (ntlmV2Hash ,
cm .ServerChallenge [:], clientChallenge )
}
return am .MarshalBinary ()
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .